package com.demo;

import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
import io.opentelemetry.context.propagation.ContextPropagators;
import io.opentelemetry.context.propagation.TextMapPropagator;
import io.opentelemetry.exporter.logging.LoggingMetricExporter;
import io.opentelemetry.exporter.logging.LoggingSpanExporter;
import io.opentelemetry.exporter.logging.SystemOutLogRecordExporter;
import io.opentelemetry.exporter.zipkin.ZipkinSpanExporter;
import io.opentelemetry.exporter.zipkin.ZipkinSpanExporterBuilder;
import io.opentelemetry.instrumentation.resources.*;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
import io.opentelemetry.sdk.logs.SdkLoggerProvider;
import io.opentelemetry.sdk.logs.export.BatchLogRecordProcessor;
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor;
import io.opentelemetry.semconv.ResourceAttributes;
import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;


import io.opentelemetry.api.OpenTelemetry;

import java.lang.management.*;
import java.util.concurrent.ThreadLocalRandom;


@SpringBootApplication
//@EnableOpenTelemetry
public class Application {

    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(Application.class);
        app.setBannerMode(Banner.Mode.OFF);
        app.run(args);
    }

    @Bean
    public OpenTelemetry openTelemetry() {
//        OpenTelemetry openTelemetry = AutoConfiguredOpenTelemetrySdk.initialize().getOpenTelemetrySdk();
//        Meter meter = openTelemetry.meterBuilder("instrumentation-library-name")
//                .setInstrumentationVersion("1.0.0")
//                .build();
//        meter
//                .gaugeBuilder("cpu_usage")
//                .setDescription("CPU Usage")
//                .setUnit("ms")
//                .buildWithCallback(measurement -> {
//                    measurement.record(getCpuUsage(), Attributes.of(AttributeKey.stringKey("Key"), "SomeWork"));
//                });
        Resource resource = Resource.getDefault()
                .merge(ContainerResource.get())
                .merge(HostResource.get())
                .merge(OsResource.get())
                .merge(ProcessResource.get())
                .merge(ProcessRuntimeResource.get())
                .merge(Resource.create(Attributes.builder().
                        put(ResourceAttributes.SERVICE_NAME, "dice-server").put(ResourceAttributes.SERVICE_VERSION, "0.1.0").build()));

        SdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder()
                .addSpanProcessor(SimpleSpanProcessor.create(LoggingSpanExporter.create()))
                .setResource(resource)
                .build();

        SdkMeterProvider sdkMeterProvider = SdkMeterProvider.builder()
                .registerMetricReader(PeriodicMetricReader.builder(LoggingMetricExporter.create()).build())
                .setResource(resource)
                .build();

        SdkLoggerProvider sdkLoggerProvider = SdkLoggerProvider.builder()
                .addLogRecordProcessor(BatchLogRecordProcessor.builder(SystemOutLogRecordExporter.create()).build())
                .setResource(resource)
                .build();

        OpenTelemetry openTelemetry = OpenTelemetrySdk.builder()
                .setTracerProvider(sdkTracerProvider)
                .setMeterProvider(sdkMeterProvider)
                .setLoggerProvider(sdkLoggerProvider)
                .setPropagators(ContextPropagators.create(TextMapPropagator.composite(W3CTraceContextPropagator.getInstance(), W3CBaggagePropagator.getInstance())))
                .buildAndRegisterGlobal();

        Meter meter = openTelemetry.meterBuilder("instrumentation-library-name")
                .setInstrumentationVersion("1.0.0")
                .build();
        meter
                .gaugeBuilder("cpu_usage")
                .setDescription("CPU Usage")
                .setUnit("ms")
                .buildWithCallback(measurement -> {
                    measurement.record(getCpuUsage(), Attributes.of(AttributeKey.stringKey("Key"), "SomeWork"));
                });

        MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
        for (GarbageCollectorMXBean gcBean : ManagementFactory.getGarbageCollectorMXBeans()) {
            if (gcBean.getName().contains("G1")) {
                System.out.println("G1垃圾收集器的名称：" + gcBean.getName());
                System.out.println("G1垃圾收集器的最大堆大小：" + gcBean.getMemoryPoolNames());
                System.out.println("G1垃圾收集器的最大堆大小：" + gcBean.getCollectionTime());
                System.out.println("G1垃圾收集器的最大堆大小：" + gcBean.getCollectionCount());
            }
        }

        // 获取堆内存使用情况
        MemoryUsage heapMemoryUsage = memoryBean.getHeapMemoryUsage();
        for (MemoryPoolMXBean memoryPool : ManagementFactory.getMemoryPoolMXBeans()) {
            if (memoryPool.getName().contains("Eden")) {
                System.out.println("新生代大小：" + memoryPool.getUsage().getUsed() / (1024 * 1024) + "MB");
                break;
            }
        }


        meter
                .gaugeBuilder("memory_usage")
                .setDescription("Memory Usage")
                .setUnit("ms")
                .buildWithCallback(measurement -> {
                    measurement.record(heapMemoryUsage.getUsed() / (1024 * 1024), Attributes.of(AttributeKey.stringKey("Key"), "memory used"));
                    measurement.record(heapMemoryUsage.getMax() / (1024 * 1024), Attributes.of(AttributeKey.stringKey("Key"), "memory max"));
                });

        return openTelemetry;
    }

    public double getCpuUsage(){
        return ThreadLocalRandom.current().nextDouble(100d);
    }

}
